package main

import (
	"fmt"
	"math/rand"
	"reflect"
	"unsafe"
)

func StrToBytes(str string) (b []byte) {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&str))
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
	return b
}

func BytesToStr(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func RandNum(min, max int) int {
	return min + rand.Intn(max-min+1)
}

func PanicError(err error) {
	if err != nil {
		panic(err)
	}
}

type DistriCounter struct {
	data []uint64
	n int
	c uint64
	lc uint64
	limit uint64
}

func NewDistricounter(size, n int, limit uint64) *DistriCounter {
	return &DistriCounter{
		data: make([]uint64, size),
		n:    n,
		limit: limit,
	}
}

func (dc *DistriCounter) Put(val uint64) {
	if val >= dc.limit {
		dc.lc++
		return
	}
	i := int(val / uint64(dc.n))
	if i >= len(dc.data) - 1 {
		temp := make([]uint64, len(dc.data) * 2)
		copy(temp, dc.data)
		dc.data = temp
	}
	dc.data[i]++
	dc.c++
}

func (dc *DistriCounter) String() string {
	m := make([][]int, 0, 10)
	for i := 0; i < len(dc.data); i++ {
		v := int(float64(dc.data[i] * 100) / float64(dc.c))
		if v > 0 {
			m  = append(m, []int{i* 10, v})
		}
	}
	if dc.lc > 0 {
		m = append(m, []int{int(dc.limit), int(float64(dc.lc * 100) / float64(dc.c))})
	}
	return fmt.Sprint(m)
}

type AvgCounter struct {
	data []uint64
	r int
	w int
	limit int
	v uint64
}

func NewCounter(n int) *AvgCounter {
	return &AvgCounter{data:make([]uint64, n * 2), limit: n}
}

func (c *AvgCounter) Put(val uint64) {
	if c.w == len(c.data)  {
		copy(c.data[0:], c.data[c.r:])
		c.w = c.w - c.r
		c.r = 0
	}

	c.data[c.w] = val
	c.w++
	c.v += val
	if c.w - c.r > c.limit {
		c.v -= c.data[c.r]
		c.r++
	}
}

func (c *AvgCounter) Value() uint64 {
	return c.v / uint64(c.limit)
}